package cn.edu.fudan.data;

import cn.edu.fudan.tools.BinarySearch;
import cn.edu.fudan.type.DataItem;
import cn.edu.fudan.type.Feature;

import java.util.*;

public class ExtractFeature {
	
//	//burst parameter
//	private long threshold_window = 20;
//	private double probability = 0.05;
//	//neighbor burst cluster as abnormal
//	private long interval = 1;
	private static BinarySearch bSearch = new BinarySearch();
	
	private double getThreshold(List<DataItem> data, int begin, int end, double probability){
		double sum = 0;
		for(int i = begin; i < end; i ++){
			sum += data.get(i).getValue();
		}
		double avg = sum/((end-begin)+1);
		double threshold = avg*Math.log(probability);
		return -threshold;
	}
	
	private List<DataItem> getBurst(List<DataItem> data, int begin, int end, double threshold){
		List<DataItem> Burst = new ArrayList<>();
		for(int i = begin; i < end; i ++){
			if(data.get(i).getValue() > threshold){
				Burst.add(data.get(i));
			}
		}
		return Burst;
	}
	
	private List<DataItem> getAbnormal(List<DataItem> burst, long interval){
		
		List<DataItem> abnormal = new ArrayList<>();
		int mark = 0;
		
		if(burst.size() == 1){
			abnormal.addAll(burst);
			return abnormal;
		}
		
		for(int i = 1; i < burst.size(); i ++ ){
			if(burst.get(i).getTimestamp() - burst.get(i-1).getTimestamp() >= interval){
				DataItem di = new DataItem();
				di.setTimestamp(0);
				di.setValue(0);
				
				for(int j = mark; j <= i-1; j ++){					
					if(burst.get(j).getValue() > di.getValue()){
						di = burst.get(j);
					}
				}
				
				abnormal.add(di);
				mark = i;
			}
			if(i == burst.size()-1 && burst.get(i).getTimestamp() - burst.get(i-1).getTimestamp() < interval){
				DataItem di = new DataItem();
				di.setTimestamp(0);
				di.setValue(0);
				
				for(int j = mark; j < burst.size(); j ++){					
					if(burst.get(j).getValue() > di.getValue()){
						di = burst.get(j);
					}
				}
				
				abnormal.add(di);
			}
		}
		
		return abnormal;
	}
	
	public Feature getFeature(List<DataItem> data, long threshold_window, double probability, long interval){
		//得到threshold，burst，abnormal
		Feature feature = new Feature();
		List<DataItem> threshold = new ArrayList<>();
		List<DataItem> bursts = new ArrayList<>();
		
		if(data.size() <= 1){
			return feature;
		}
		
		long overlap = threshold_window/2;
				
		int index_mid = bSearch.binarySearch(data, data.get(0).getTimestamp()+overlap, 0); //滑动窗中点
		int index_end = bSearch.binarySearch(data, data.get(0).getTimestamp()+threshold_window, 0); //滑动窗末点
		
		double thres_ini = getThreshold(data, 0, index_end, probability);
		DataItem di = new DataItem();
		di.setTimestamp(0);
		di.setValue(thres_ini);
		threshold.add(di);
		List<DataItem> burst = getBurst(data, 0, index_mid, thres_ini);
		if(burst.size() > 0){
			bursts.addAll(burst);
		}
		
		int beginIndex = index_mid;
		//滑动窗找burst
		for(int i = beginIndex; i < data.size(); ){
			long endTime = data.get(i).getTimestamp() + threshold_window;
			if(endTime > data.get(data.size()-1).getTimestamp()){
				break;
			}
			int mark_end = index_end;
			index_end = bSearch.binarySearch(data, endTime, 0);			
			di = new DataItem();
			di.setTimestamp(index_mid);
			double thres = getThreshold(data, index_mid, index_end, probability);
			di.setValue((thres+thres_ini)/2);
			threshold.add(di);
			burst = getBurst(data, index_mid, mark_end, di.getValue());
			if(burst.size() > 0){
				bursts.addAll(burst);
			}
			
			thres_ini = thres;			
			index_mid = mark_end;
			i = index_mid;
		}
		
		di = new DataItem();
		di.setTimestamp(index_mid);
		di.setValue(thres_ini);
		threshold.add(di);
		burst = getBurst(data, index_mid, index_end, thres_ini);
		if(burst.size() > 0){
			bursts.addAll(burst);
		}
		feature.setThreshold(threshold);
		feature.setBurst(bursts);
		feature.setAbnormal(getAbnormal(bursts, interval));
		
		return feature;
	}

	private double getAverage(List<DataItem> data, int begin, int end){ // get average of a window
		double sum = 0;
		for(int i = begin; i < end; i ++){
			sum += data.get(i).getValue();
		}
		double avg = sum/((end-begin)+1);
		return avg;
	}

	private List<DataItem> setAllAverage(List<DataItem> data, int begin, int end, double average){  //useless
		//set all values of timestamp between begin and end to average
		for(int i = begin; i < end; i ++){
			data.get(i).setValue(average);
		}
		return  data;
	}

	public Feature getPaaFeature(List<DataItem> data, long threshold_window){
		//得到窗口内的PAA表示,每个均值表示最初的时间戳的值
		//这里的threshold长度是每段段的长度，用来求平均
		//使得每个段的中点的时间戳的值是paa的均值，这样可以得到n--段数的维度的特征
		Feature feature = new Feature();
		List<DataItem> threshold = new ArrayList<>();
		List<DataItem> bursts = new ArrayList<>();
		List<DataItem> paapre = new ArrayList<>();



		if(data.size() <= 1){
			return feature;
		}

		long overlap = threshold_window/2;

		int index_mid = bSearch.binarySearch(data, data.get(0).getTimestamp()+overlap, 0); //滑动窗中点
		int index_end = bSearch.binarySearch(data, data.get(0).getTimestamp()+threshold_window, 0); //滑动窗末点

		double avg_ini = getAverage(data, 0, index_end);
		DataItem di = new DataItem();
		di.setTimestamp(index_mid);
		di.setValue(avg_ini);
		paapre.add(di);


		int beginIndex = index_end;
		for(int i = 1 ; i < data.size()/threshold_window-1; i++){
			index_mid = bSearch.binarySearch(data, data.get(beginIndex).getTimestamp()+overlap, 0);
			index_end = bSearch.binarySearch(data, data.get(beginIndex).getTimestamp()+threshold_window, 0);
			double avg = getAverage(data, beginIndex, index_end);
			di.setTimestamp(index_mid);
			di.setValue(avg);
			paapre.add(di);
			beginIndex = index_end;
		}
		index_mid = bSearch.binarySearch(data, data.get((data.size()-1+beginIndex)/2).getTimestamp(), 0);
		index_end = bSearch.binarySearch(data, data.get(data.size()-1).getTimestamp(), 0);
		double avg = getAverage(data, beginIndex, index_end);
		di.setTimestamp(index_mid);
		di.setValue(avg);
		paapre.add(di);
		feature.setPaaPresetation(paapre);
		return feature;
	}

}
